using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.IO;
using System.Xml.Serialization;

namespace OneStop.Calculations
{
    #region Enums

    public enum Units
    {
        Metric = 0,
        Imperial = 1
    }

    #endregion

    /// <summary>
    /// Represents all the preference options.
    /// </summary>
    
    public class Prefs : INotifyPropertyChanged
    {
        #region Constructors

        public Prefs()
        {
            
        }

        private static Prefs prefs;
        private static object thisLock = new object();

        public static Prefs Current
        {
            get
            {
                if (prefs == null)
                {
                    lock (thisLock)
                    {
                        prefs = new Prefs();
                    }
                }

                return prefs;
            }
        }

        #endregion
        
        #region Interface Methods

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #endregion

        #region Default Settings 

        // Unknown
        private bool _ForceAllStops = true;

        public bool ForceAllStops
        {
            get { return _ForceAllStops;  }
            set { _ForceAllStops = value; NotifyPropertyChanged("ForceAllStops"); }
        }

        private bool _OcDeco = false;

        public bool OcDeco
        {
            get { return _OcDeco; }
            set { _OcDeco = value; NotifyPropertyChanged("OcDeco"); }
        }

        private double _RMVDive = 17;

        public double RMVDive
        {
            get { return _RMVDive; }
            set { _RMVDive = value; NotifyPropertyChanged("RMVDive"); }
        }
        private double _RMVDeco = 12;

        public double RMVDeco
        {
            get { return _RMVDeco; }
            set { _RMVDeco = value; NotifyPropertyChanged("RMVDeco"); }
        }

        private double _StopDepthIncrement = 3;

        public double StopDepthIncrement
        {
            get { return _StopDepthIncrement; }
            set { _StopDepthIncrement = value; NotifyPropertyChanged("StopDepthIncrement"); }
        }
        private double _LastStopDepth = 6;

        public double LastStopDepth
        {
            get { return _LastStopDepth; }
            set { _LastStopDepth = value; NotifyPropertyChanged("LastStopDepth"); }
        }
        private double _StopTimeIncrement = 1;

        public double StopTimeIncrement
        {
            get { return _StopTimeIncrement; }
            set { _StopTimeIncrement = value; NotifyPropertyChanged("StopTimeIncrement"); }
        }

        private bool _GfMultilevelMode = false;

        public bool GfMultilevelMode
        {
            get { return _GfMultilevelMode; }
            set { _GfMultilevelMode = value; NotifyPropertyChanged("GfMultilevelMode"); }
        }
        private bool _RuntimeFlag = true;

        public bool RuntimeFlag
        {
            get { return _RuntimeFlag; }
            set { _RuntimeFlag = value; NotifyPropertyChanged("RuntimeFlag"); }
        }

        public Gradient Gradient
        {
            get
            {
                return new Gradient(this.GfLow, this.GfHigh);
            }
        }

        private double _GfLow = 0.15;

        public double GfLow
        {
            get { return _GfLow; }
            set { _GfLow = value; NotifyPropertyChanged("GfLow"); NotifyPropertyChanged("Gradient"); }
        }
        private double _GfHigh = 0.85;

        public double GfHigh
        {
            get { return _GfHigh; }
            set { _GfHigh = value; NotifyPropertyChanged("GfHigh"); NotifyPropertyChanged("Gradient"); }
        }

        public Gradient GradientBailout
        {
            get
            {
                return new Gradient(this.GfLowBailout, this.GfHighBailout);
            }
        }

        private double _GfLowBailout = 0.95;

        public double GfLowBailout
        {
            get { return _GfLowBailout; }
            set { _GfLowBailout = value; NotifyPropertyChanged("GfLowBailout"); NotifyPropertyChanged("GradientBailout"); }
        }
        private double _GfHighBailout = 0.95;

        public double GfHighBailout
        {
            get { return _GfHighBailout; }
            set { _GfHighBailout = value; NotifyPropertyChanged("GfHighBailout"); NotifyPropertyChanged("GradientBailout"); }
        }

        private double _AscentRate = -10;

        public double AscentRate
        {
            get { return _AscentRate; }
            set { _AscentRate = value; NotifyPropertyChanged("AscentRate"); }
        }
        private double _DescentRate = 20;

        public double DescentRate
        {
            get { return _DescentRate; }
            set { _DescentRate = value; NotifyPropertyChanged("DescentRate"); }
        }

        private double _SetpointDefault = 0;

        public double SetpointDefault
        {
            get { return _SetpointDefault; }
            set { _SetpointDefault = value; NotifyPropertyChanged("SetpointDefault"); }
        }

        private double _PPO2Max = 1.6;

        public double PPO2Max
        {
            get { return _PPO2Max; }
            set { _PPO2Max = value; NotifyPropertyChanged("PPO2Max"); }
        }
        private double _PPO2Min = 0.21;

        public double PPO2Min
        {
            get { return _PPO2Min; }
            set { _PPO2Min = value; NotifyPropertyChanged("PPO2Min"); }
        }

        private double _WarningPPO2Max = 1.4;

        public double WarningPPO2Max
        {
            get { return _WarningPPO2Max; }
            set { _WarningPPO2Max = value; NotifyPropertyChanged("WarningPPO2Max"); }
        }
        private double _WarningPPO2Min = 0.21;

        public double WarningPPO2Min
        {
            get { return _WarningPPO2Min; }
            set { _WarningPPO2Min = value; NotifyPropertyChanged("WarningPPO2Min"); }
        }
        private double _WarningPPN2SpikePercentageMin = 0.01;

        public double WarningPPN2SpikePercentageMin
        {
            get { return _WarningPPN2SpikePercentageMin; }
            set { _WarningPPN2SpikePercentageMin = value; NotifyPropertyChanged("WarningPPN2SpikePercentageMin"); }
        }
        private double _WarningOptimalDecoPPO2Max = 1.6;

        public double WarningOptimalDecoPPO2Max
        {
            get { return _WarningOptimalDecoPPO2Max; }
            set { _WarningOptimalDecoPPO2Max = value; NotifyPropertyChanged("WarningOptimalDecoPPO2Max"); }
        }
        private double _WarningOptimalDecoPPO2Min = 1.0;

        public double WarningOptimalDecoPPO2Min
        {
            get { return _WarningOptimalDecoPPO2Min; }
            set { _WarningOptimalDecoPPO2Min = value; NotifyPropertyChanged("WarningOptimalDecoPPO2Min"); }
        }

        private double _ReportMinStopTime = 0.5;

        public double ReportMinStopTime
        {
            get { return _ReportMinStopTime; }
            set { _ReportMinStopTime = value; NotifyPropertyChanged("ReportMinStopTime"); }
        }
        private bool _ReportShowStopTime = true;

        public bool ReportShowStopTime
        {
            get { return _ReportShowStopTime; }
            set { _ReportShowStopTime = value; NotifyPropertyChanged("ReportShowStopTime"); }
        }
        private bool _ReportShowBailout = true;

        public bool ReportShowBailout
        {
            get { return _ReportShowBailout; }
            set { _ReportShowBailout = value; NotifyPropertyChanged("ReportShowBailout"); }
        }

        private Units _Units = Units.Metric;

        public Units Units
        {
            get { return _Units; }
            set { _Units = value; NotifyPropertyChanged("Units"); }
        }

        // Metric
        //public double PConversion = 10;
        //public double PH2O = 0.627;
        //public double PAmb = 10;
        // Imperial
        //public double PConversion = 33.0;
        //public double PH2O = 2.041;
        //public double PAmb = 33.0;

        public string DepthShortString
        {
            get
            {
                return (this.Units == Units.Metric) ? "m" : "ft";
            }
        }

        public double PConversion
        {
            get
            {
                return (this.Units == Units.Metric) ? 10 : 33;
            }
        }

        public double PH2O
        {
            get
            {
                return (this.Units == Units.Metric) ? 0.627 : 2.041;
            }
        }

        public double PAmb
        {
            get
            {
                return (this.Units == Units.Metric) ? 10 : 33;
            }
        }

        public bool IsWarningEnabled(WarningType type)
        {            
            //NotSet,
            //PPO2High,
            //PPO2Low,
            //PPN2Spike,
            //SubOptimalDecoPPO2,
            //InsufficientOCGas

            bool[] warnings = new bool[] { false, true, true, true, true, true };

            return warnings[(int)type];
        }

        #endregion

        #region Static Methods

        /// <summary>
        /// Serialises the Prefs singleton to the output stream. 
        /// </summary>
        /// <param name="output"></param>
        public static void Serialize(Stream output)
        {
            XmlSerializer s = new XmlSerializer(typeof(Prefs));

            // To write to a file, create a StreamWriter object.
            StreamWriter writer = new StreamWriter(output);

            s.Serialize(writer, Prefs.Current);
        }

        /// <summary>
        /// Instantiates an Prefs singleton from XML.
        /// </summary>
        /// <param name="input"></param>
        public static void Deserialize(Stream input)
        {
            XmlSerializer s = new XmlSerializer(typeof(Prefs));

            lock (thisLock)
            {
                Prefs.prefs = (Prefs)s.Deserialize(input);
            }
        }

        public static void Reset()
        {
            lock (thisLock)
            {
                Prefs.prefs = new Prefs();
            }
        }

        #endregion
    }
}
